# Copyright (C) 2015 Free Software Foundation, Inc.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Check that "gdb -batch -ex run" does not leave the terminal in the
# wrong state.

standard_testfile

if {[build_executable "failed to prepare" $testfile $srcfile debug] == -1} {
    return -1
}

set file_arg $binfile
if [is_remote host] {
  set file_arg [remote_download host $file_arg]
}

# The shell's prompt.
set shell_prompt "$ "
set shell_prompt_re [string_to_regexp $shell_prompt]

# Spawn shell.  Returns true on success, false otherwise.

proc spawn_shell {} {
    global shell_prompt_re

    set res [remote_spawn host "/bin/sh"]
    if { $res < 0 || $res == "" } {
	unsupported "Spawning shell failed."
	return 0
    }

    set gotit 0
    set test "spawn shell"
    gdb_expect {
	-re "$shell_prompt_re$" {
	    pass $test
	    set gotit 1
	}
	timeout {
	    fail "$test (timeout)"
	}
	eof {
	    fail "$test (eof)"
	}
    }

    return $gotit
}

# Exit the shell.

proc exit_shell {} {
    global shell_prompt_re

    set test "exit shell"
    send_gdb "exit\n"
    gdb_expect {
	timeout {
	    fail "$test (timeout)"
	    return 0
	}
	eof {
	    pass "$test"
	}
    }
    if ![is_remote host] {
	remote_close host
    }
}

# Run "stty" and store the output in $result.  Returns true on
# success, false otherwise.

proc run_stty {message result} {
    global shell_prompt_re

    upvar $result output

    send_gdb "stty || echo \"not found\"\n"
    set gotit 0
    gdb_expect {
	-re "not found.*not found.*$shell_prompt_re$" {
	    pass "$message (not found)"
	}
	-re "(.*)$shell_prompt_re$" {
	    set output $expect_out(1,string)
	    set gotit 1
	    pass $message
	}
	timeout {
	    fail "$message (timeout)"
	}
	eof {
	    fail "$message (eof)"
	}
    }
    return $gotit
}

# Check that "gdb -batch -ex run" does not leave the terminal in the
# wrong state.

proc test_terminal_settings_preserved {} {
    global file_arg
    global GDB INTERNAL_GDBFLAGS GDBFLAGS
    global gdb_prompt
    global shell_prompt_re

    if ![spawn_shell] {
	return
    }

    set stty_supported [run_stty "stty before" stty_before]

    set test "gdb -batch -ex run"
    append EXTRA_GDBFLAGS "-batch"
    append EXTRA_GDBFLAGS " -ex \"set height unlimited\""
    append EXTRA_GDBFLAGS " -ex \"start\""
    append EXTRA_GDBFLAGS " --args \"$file_arg\""
    send_gdb "$GDB $INTERNAL_GDBFLAGS $GDBFLAGS $EXTRA_GDBFLAGS [host_info gdb_opts]\n"
    gdb_expect {
	-re "Don't know how to run.*$shell_prompt_re$" {
	    unsupported $test
	}
	-re "$gdb_prompt $" {
	    # -batch implies no GDB prompt.
	    fail $test
	}
	-re "Temporary breakpoint .*$shell_prompt_re$" {
	    pass $test
	}
	timeout {
	    fail "$test (timeout)"
	}
	eof {
	    fail "$test (eof)"
	}
    }

    set test "echo test_echo"
    send_gdb "echo test_echo\n"
    gdb_expect {
	-re "^echo test_echo\r\ntest_echo\r\n.*$shell_prompt_re$" {
	    pass $test
	}
	timeout {
	    fail "$test (timeout)"
	}
	eof {
	    fail "$test (eof)"
	}
    }

    set test "terminal settings preserved"
    if $stty_supported {
	run_stty "stty after" stty_after

	gdb_assert [string equal $stty_before $stty_after] $test
    } else {
	unsupported "$test (no stty)"
    }

    exit_shell
}

# Send the quit command to GDB and make sure it exits.

proc send_quit_command { test_name } {
    global shell_prompt_re

    set test $test_name
    send_gdb "quit\n"
    gdb_expect {
	-re "(y or n)" {
	    send_gdb "y\n"
	    exp_continue
	}
	-re ".*$shell_prompt_re$" {
	    pass $test
	    return
	}
	timeout {
	    fail "$test (timeout)"
	    return 0
	}
	eof {
	    fail "$test (eof)"
	    return 0
	}
    }
}

# Check that quitting from the CLI via the "quit" command does not leave the
# terminal in the wrong state.  The GDB commands CMDS are executed before
# quitting.

proc test_terminal_settings_preserved_after_cli_exit { cmds } {
    global file_arg
    global GDB INTERNAL_GDBFLAGS GDBFLAGS
    global gdb_prompt
    global shell_prompt_re

    if ![spawn_shell] {
	return
    }

    set saved_gdbflags $GDBFLAGS

    set stty_supported [run_stty "stty before" stty_before]

    set test "start gdb"
    send_gdb "$GDB $INTERNAL_GDBFLAGS $GDBFLAGS [host_info gdb_opts] --args \"$file_arg\"\n"
    gdb_expect {
	-re "$gdb_prompt $" {
	    pass $test
	}
	timeout {
	    fail "$test (timeout)"
	}
	eof {
	    fail "$test (eof)"
	}
    }

    foreach cmd $cmds {
	set test "run command $cmd"
	send_gdb "$cmd\n"
	gdb_expect {
	    -re "$gdb_prompt $" {
		pass $test
	    }
	    timeout {
		fail "$test (timeout)"
	    }
	    eof {
		fail "$test (eof)"
	    }
	}
    }

    send_quit_command "quit gdb"

    set test "terminal settings preserved"
    if $stty_supported {
	run_stty "stty after" stty_after

	gdb_assert [string equal $stty_before $stty_after] $test
    } else {
	unsupported "$test (no stty)"
    }

    exit_shell
}

# Check that sending SIGTERM kills GDB and does not leave the terminal in the
# wrong state.

proc test_terminal_settings_preserved_after_sigterm { } {
    global file_arg
    global GDB INTERNAL_GDBFLAGS GDBFLAGS
    global gdb_prompt
    global shell_prompt_re

    if ![spawn_shell] {
	return
    }

    set saved_gdbflags $GDBFLAGS

    set stty_supported [run_stty "stty before" stty_before]

    set test "start gdb"
    send_gdb "$GDB $INTERNAL_GDBFLAGS $GDBFLAGS [host_info gdb_opts]\n"
    gdb_expect {
	-re "$gdb_prompt $" {
	    pass $test
	}
	timeout {
	    fail "$test (timeout)"
	}
	eof {
	    fail "$test (eof)"
	}
    }

    # Retrieve the pid of gdb with the gdb command "shell echo $PPID"
    set gdb_pid -1
    set test "run shell echo \$PPID"
    send_gdb "shell echo \$PPID\n"
    gdb_expect {
	-re ".*\r\n(\\d+)\r\n$gdb_prompt $" {
	    set gdb_pid $expect_out(1,string)
	    pass $test
	}
	-re ".*\r\n\r\n$gdb_prompt $" {
	    fail "$test (no \$PPID)"
	}
	timeout {
	    fail "$test (timeout)"
	}
	eof {
	    fail "$test (eof)"
	}
    }

    set test "kill gdb with SIGTERM"
    if { $gdb_pid == -1 } {
	fail "$test (no pid)"
	send_quit_command "quit gdb"
    } else {
	remote_exec host "kill -TERM $gdb_pid"
	set gdb_killed 0
	gdb_expect {
	    -re ".*$shell_prompt_re$" {
		pass $test
		set gdb_killed 1
	    }
	    default {
		fail "$test (did not quit)"
	    }
	}

	if !$gdb_killed {
	    send_quit_command "quit gdb"
	}
    }

    set test "terminal settings preserved"
    if $stty_supported {
	run_stty "stty after" stty_after

	gdb_assert [string equal $stty_before $stty_after] $test
    } else {
	unsupported "$test (no stty)"
    }

    exit_shell
}

with_test_prefix "batch run" {
    test_terminal_settings_preserved
}

with_test_prefix "cli exit" {
    test_terminal_settings_preserved_after_cli_exit { }
}

with_test_prefix "cli exit after start cmd" {
    test_terminal_settings_preserved_after_cli_exit { "start" }
}

with_test_prefix "cli exit after run cmd" {
    test_terminal_settings_preserved_after_cli_exit { "run" }
}

with_test_prefix "cli exit after SIGTERM" {
    test_terminal_settings_preserved_after_sigterm
}
